// 指定包名，归类控制器层
package com.example.ossmultipart.controller; // 分片上传控制器所在包

// 导入 Spring Web 注解与类型
import org.springframework.web.bind.annotation.*; // 导入 REST 控制器与请求映射注解
import org.springframework.http.MediaType; // 导入媒体类型常量
import org.springframework.web.multipart.MultipartFile; // 导入 MultipartFile 以接收分片数据

// 导入服务类与模型类
import com.example.ossmultipart.service.OssMultipartService; // 导入分片上传服务
import com.example.ossmultipart.dto.InitiateRequest; // 导入初始化请求体
import com.example.ossmultipart.dto.CompleteRequest; // 导入完成合并请求体
import com.example.ossmultipart.dto.AbortRequest; // 导入终止请求体
import com.example.ossmultipart.dto.PartDto; // 导入分片信息 DTO
import com.example.ossmultipart.util.Msg; // 导入统一返回类（util 包）

// 导入阿里云 OSS 分片标识类
import com.aliyun.oss.model.PartETag; // 导入分片标识类

// 导入 Java 常用集合与工具
import java.io.InputStream; // 导入输入流以读取分片数据
import java.util.*; // 导入集合与工具类
import org.slf4j.Logger; // 日志记录器
import org.slf4j.LoggerFactory; // 日志记录器工厂

// 声明为 REST 控制器，基础路径为 /api/multipart
@RestController // 声明为 REST 风格的控制器
@RequestMapping("/api/multipart") // 统一的接口前缀
public class MultipartController { // 定义控制器类
    private static final Logger log = LoggerFactory.getLogger(MultipartController.class); // 创建日志记录器

    // 注入服务类以执行业务逻辑
    private final OssMultipartService multipartService; // 声明服务类成员

    // 使用构造器注入服务类，利于测试与不可变性
    public MultipartController(OssMultipartService multipartService) { // 控制器构造方法
        this.multipartService = multipartService; // 赋值服务实例
    } // 构造方法结束

    // 初始化分片上传接口：返回 uploadId 与 objectName
    @PostMapping("/initiate") // POST 映射：初始化分片上传
    public Msg<Map<String, String>> initiate(@RequestBody InitiateRequest request) { // 接收 JSON 请求体
        try { // 捕获异常以返回统一失败信息
            // 从请求体获取文件名与内容类型
            String filename = request.getFilename(); // 取文件名
            String contentType = request.getContentType(); // 取内容类型
            // 调用服务层初始化分片上传
            Map<String, String> result = multipartService.initiateMultipartUpload(filename, contentType); // 执行业务
            log.info("Initiate API: filename={}, contentType={}, uploadId={}, objectName={}", filename, contentType, result.get("uploadId"), result.get("objectName")); // 打印初始化信息
            // 返回统一成功结果（携带 uploadId 与 objectName）
            return Msg.success("初始化成功", result); // 返回成功消息
        } catch (Exception e) { // 捕获到异常
            // 返回统一失败结果，附带错误信息
            return Msg.fail("初始化失败：" + e.getMessage()); // 返回失败消息
        } // try-catch 结束
    } // 方法结束

    // 上传单个分片接口：接收 multipart 表单，返回该分片的 ETag
    @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) // POST 映射：上传分片，消费 multipart/form-data
    public Msg<Map<String, Object>> uploadPart(
            @RequestParam("uploadId") String uploadId, // 上传会话 ID
            @RequestParam("objectName") String objectName, // 对象名（路径）
            @RequestParam("partNumber") int partNumber, // 分片号（1~10000）
            @RequestPart("file") MultipartFile file // 分片二进制数据
    ) { // 方法开始
        try { // 捕获异常以返回统一失败信息
            // 从 MultipartFile 获取分片大小（字节）
            long partSize = file.getSize(); // 获取分片大小
            // 使用 try-with-resources 确保输入流在上传后自动关闭，避免资源泄漏
            try (InputStream is = file.getInputStream()) { // 获取分片输入流并自动管理关闭
                // 调用服务层上传分片，获得服务端返回的 ETag
                String eTag = multipartService.uploadPart(uploadId, objectName, partNumber, is, partSize); // 执行业务
                // 封装返回数据：分片号与 ETag
                Map<String, Object> data = new HashMap<>(); // 创建返回 Map
                data.put("partNumber", partNumber); // 放入分片号
                data.put("eTag", eTag); // 放入 ETag
                log.info("Upload API: objectName={}, uploadId={}, partNumber={}, partSize={}, eTag={}", objectName, uploadId, partNumber, partSize, eTag); // 打印分片上传信息
                // 返回统一成功结果
                return Msg.success("分片上传成功", data); // 返回成功消息
            } // 输入流自动关闭
        } catch (Exception e) { // 捕获到异常
            log.warn("Upload API failed: objectName={}, uploadId={}, partNumber={}, error={}", objectName, uploadId, partNumber, e.getMessage()); // 打印错误信息
            // 返回统一失败结果，附带错误信息
            return Msg.fail("分片上传失败：" + e.getMessage()); // 返回失败消息
        } // try-catch 结束
    } // 方法结束

    // 完成分片上传接口：服务端合并所有分片
    @PostMapping("/complete") // POST 映射：完成分片合并
    public Msg<Map<String, String>> complete(@RequestBody CompleteRequest request) { // 接收 JSON 请求体
        try { // 捕获异常以返回统一失败信息
            // 从请求体获取必要信息
            String uploadId = request.getUploadId(); // 获取 uploadId
            String objectName = request.getObjectName(); // 获取对象名
            List<PartDto> parts = request.getParts(); // 获取分片信息列表
            log.info("Complete API received: objectName={}, uploadId={}, partsCount={}", objectName, uploadId, parts == null ? 0 : parts.size()); // 打印合并请求信息
            // 先验证会话是否存在，避免 NoSuchUpload 误用（例如 uploadId/objectName 不一致）
            boolean exists = multipartService.verifyUploadSession(uploadId, objectName); // 校验会话
            if (!exists) { // 会话不存在或已终止
                return Msg.fail("分片合并失败：上传会话不存在或已终止（NoSuchUpload）。请确认 uploadId 与 objectName 与初始化返回值一致，且未执行取消/超时。"); // 返回更明确的错误信息
            }
            // 将分片信息转换为 OSS 的 PartETag 列表；如果前端传来的 eTag 为空或缺失，退化为服务端列举以保证合并成功
            List<PartETag> partETags = new ArrayList<>(); // 创建 PartETag 列表
            boolean needFallback = (parts == null || parts.isEmpty()); // 初始判断是否需要服务端回退
            if (!needFallback) {
                for (PartDto p : parts) { // 遍历分片信息
                    String et = p.getETag(); // 取 eTag
                    if (et == null || et.trim().isEmpty()) { // 前端可能未传或字段名不匹配导致为 null
                        needFallback = true; // 标记需要回退
                        log.warn("Complete API: part eTag is null/blank, will fallback to server list. objectName={}, uploadId={}, partNumber={}",
                                objectName, uploadId, p.getPartNumber());
                        break;
                    }
                    // 去除可能包含的双引号，避免 SDK 内部字符串处理异常（安全处理，不影响合法值）
                    String sanitized = et.trim();
                    if (sanitized.length() >= 2 && sanitized.startsWith("\"") && sanitized.endsWith("\"")) {
                        sanitized = sanitized.substring(1, sanitized.length() - 1);
                    }
                    partETags.add(new PartETag(p.getPartNumber(), sanitized)); // 构建 PartETag
                } // for 结束
            }

            if (needFallback) { // 使用服务端列举到的分片信息进行合并，保证 eTag 不为 null
                List<PartETag> serverEtags = multipartService.listServerPartETags(uploadId, objectName);
                if (serverEtags == null || serverEtags.isEmpty()) {
                    return Msg.fail("分片合并失败：服务端未找到已上传的分片，请确认 /upload 阶段已成功并返回了 eTag。");
                }
                partETags = serverEtags; // 采用服务端分片列表
                log.info("Complete API: using server-listed partETags, count={}", partETags.size());
            }
            // 按分片号升序排序（OSS 合并时需要按分片号顺序）
            partETags.sort(Comparator.comparingInt(PartETag::getPartNumber)); // 排序 PartETag 列表
            // 调用服务层完成分片合并，返回最终对象 ETag（可选）
            String finalETag = multipartService.completeMultipartUpload(uploadId, objectName, partETags); // 执行业务
            // 构造返回数据：最终 ETag 与对象名
            Map<String, String> data = new HashMap<>(); // 创建返回 Map
            data.put("finalETag", finalETag); // 放入最终 ETag
            data.put("objectName", objectName); // 放入对象名
            // 返回统一成功结果
            return Msg.success("分片合并完成", data); // 返回成功消息
        } catch (Exception e) { // 捕获到异常
            log.warn("Complete API failed: objectName={}, uploadId={}, error={}", request.getObjectName(), request.getUploadId(), e.getMessage()); // 打印错误信息
            // 返回统一失败结果，附带错误信息
            return Msg.fail("分片合并失败：" + e.getMessage()); // 返回失败消息
        } // try-catch 结束
    } // 方法结束

    // 终止（取消）分片上传接口：清理已上传分片
    @PostMapping("/abort") // POST 映射：终止分片上传
    public Msg<Void> abort(@RequestBody AbortRequest request) { // 接收 JSON 请求体
        try { // 捕获异常以返回统一失败信息
            // 从请求体获取 uploadId 与对象名
            String uploadId = request.getUploadId(); // 获取 uploadId
            String objectName = request.getObjectName(); // 获取对象名
            // 调用服务层执行终止，清理已上传分片
            multipartService.abortMultipartUpload(uploadId, objectName); // 执行业务
            log.warn("Abort API: objectName={}, uploadId={}", objectName, uploadId); // 打印终止信息
            // 返回统一成功结果（无数据体）
            return Msg.success("已终止分片上传"); // 返回成功消息
        } catch (Exception e) { // 捕获到异常
            log.warn("Abort API failed: objectName={}, uploadId={}, error={}", request.getObjectName(), request.getUploadId(), e.getMessage()); // 打印错误信息
            // 返回统一失败结果，附带错误信息
            return Msg.fail("终止失败：" + e.getMessage()); // 返回失败消息
        } // try-catch 结束
    } // 方法结束
} // 控制器类结束